package org.budo.support.lang.util;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.budo.support.assertion.Assert;
import org.budo.support.slf4j.Slf4j;
import org.slf4j.Logger;

/**
 * @author fuqinqin
 */
public class BeanUtil {
    private static Logger log = Slf4j.getLogger();

    /**
     * 对象拷贝
     * */
    public static <T> T copyProperties(Object source, Class<T> clazz){
        return copyProperties(source, clazz, null);
    }
    public static <T> T copyProperties(Object source, Class<T> clazz, Class<? extends Annotation> annotation) {
        Assert.notNull(source, "source must not be null");
        Assert.notNull(clazz, "clazz must not be null");

        T target = null;
        try {
            target = clazz.newInstance();
            boolean allSatisfied = false;
            if(annotation == null){
                allSatisfied = true;
            }else if(ReflectUtil.hasAnnotationPresent(clazz, annotation)){
                allSatisfied = true;
            }

            List<Field> allFields = ReflectUtil.getAllFields(clazz);
            if(CollectionUtil.isEmpty(allFields)){
                return (T) target;
            }

            Set<String> sourceAttribute = ReflectUtil.getAllAttribute(source.getClass());

            String fieldName = null;
            for (Field field : allFields) {
                fieldName = field.getName();

                // 排除被 static 和 final 关键字修饰的属性
                if(ReflectUtil.isStatic(field) || ReflectUtil.isFinal(field)){
                    continue;
                }

                if(!sourceAttribute.contains(fieldName)){
                    continue;
                }

                if(allSatisfied || ReflectUtil.hasAnnotationPresent(field, annotation)){
                    ReflectUtil.invokeSet(target, field, ReflectUtil.invokeGet(source, fieldName));
                }
            }
        } catch (InstantiationException e) {
            log.error("copyProperties error", e);
        } catch (IllegalAccessException e) {
            log.error("copyProperties error", e);
        }

        return target;
    }

    /**
     * 集合拷贝
     * @param sourceList 源数据
     * @param targetClass 返回的数据类型
     * @param annotation 用户自定义注解，注解可以作用于类或者属性，如果作用于类，则全部属性拷贝；如果作用于部分属性，则只拷贝部分属性
     * */
    public static <T> List<T> copyList(Collection<? extends Object> sourceList, Class<T> targetClass, Class<? extends Annotation> annotation){
        List<T> targetList = new ArrayList<>();
        if(CollectionUtil.isEmpty(sourceList)){
            return targetList;
        }

        T t;
        for (Object source : sourceList) {
            t = copyProperties(source, targetClass, annotation);
            targetList.add(t);
        }

        return targetList;
    }
    public static <T> List<T> copyList(Collection<? extends Object> sourceList, Class<T> targetClass){
        return copyList(sourceList, targetClass, null);
    }

    /**
     * 将对象转换成Map
     * */
    public static <T> Map<String, Object> bean2Map(T source){
        return bean2Map(source, null);
    }
    public static <T> Map<String, Object> bean2Map(T source, Class<? extends Annotation> annotation){
        Assert.notNull(source, "source must not be null");

        Map<String, Object> result = new HashMap<>();

        Class clazz = source.getClass();
        boolean allSatisfied = false;
        if(annotation == null){
            allSatisfied = true;
        }else if(ReflectUtil.hasAnnotationPresent(clazz, annotation)){
            allSatisfied = true;
        }

        List<Field> allFields = ReflectUtil.getAllFields(clazz);
        if(CollectionUtil.isEmpty(allFields)){
            return result;
        }

        String fieldName = null;
        for (Field field : allFields) {
            fieldName = field.getName();

            // 排除被 static 和 final 关键字修饰的属性
            if(ReflectUtil.isStatic(field) || ReflectUtil.isFinal(field)){
                continue;
            }

            if(allSatisfied || ReflectUtil.hasAnnotationPresent(field, annotation)){
                Object obj = ReflectUtil.invokeGet(source, fieldName);
                if(obj != null) {
                    result.put(fieldName, obj);
                }
            }
        }

        return result;
    }
}
